#install.packages("knitr")
#install.packages("grid")
install.packages("MLRMPA")
Installing package into 㤼㸱C:/Users/ASUS/Documents/R/win-library/4.0㤼㸲
(as 㤼㸱lib㤼㸲 is unspecified)
Warning in install.packages :
package ‘MLRMPA’ is not available (for R version 4.0.2)
#install.packages("dprep")
#install.packages("normalr")
#install.packages("ggcorrplot")
library(gridExtra)
library(dplyr)
library(lubridate)
library(magrittr)
library(ggplot2)
library(tidyr)
library(knitr)
library(normalr)
library(ggcorrplot)
#library(MLRMPA)
#??src_mysql
my_db <- src_mysql(
dbname = "coronavirus",
host = "localhost",
user = "root",
password = "1234"
)
my_db
src: mysql 8.0.21 [root@localhost:/coronavirus]
tbls: avg_world_temp_2020, covid19_confirmed, covid19_deaths, covid19_recovered, gdp, gdp19,
healthranking, population, pornhub, sars, sars_2003, sars_2003_update, top20pornhub
##import data
df_conf <- tbl(my_db, sql("select * from covid19_confirmed "))
df_conf <- as.data.frame(df_conf)
df_conf
df_deaths <- tbl(my_db, sql("select * from covid19_deaths "))
df_deaths <- as.data.frame(df_deaths)
df_deaths
df_recover <- tbl(my_db, sql("select * from covid19_recovered "))
df_recover <- as.data.frame(df_recover)
df_recover
##check the time frame of the data
n.col <- ncol(df_conf)
dates <- names(df_conf)[5:n.col]%>% mdy()
range(dates)
[1] "2020-01-22" "2021-01-14"
min.date <- min(dates)
max.date <- max(dates)
min.date.txt <- min.date %>% format('%d %b %Y')
max.date.txt <- max.date %>% format('%d %b %Y')
#clean data
cleanData <- function(data) {
## remove some columns
data %<>% select(-c(Province.State, Lat, Long)) %>% rename(country=Country.Region)
## convert from wide to long format
data %<>% gather(key=date, value=count, -country)
## convert from character to date
data %<>% mutate(date = date %>% mdy())
## aggregate by country
data %<>% group_by(country, date) %>% summarise(count=sum(count, na.rm=T)) %>% as.data.frame()
return(data)
}
## clean the three data sets
data.confirmed <- df_conf %>% cleanData() %>% rename(confirmed=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.deaths <- df_deaths %>% cleanData() %>% rename(deaths=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.recovered <- df_recover %>% cleanData() %>% rename(recovered=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data <- data.confirmed %>% merge(data.deaths, all=T) %>% merge(data.recovered, all=T)
data
## countries/regions with confirmed cases, excl. cruise ships
countries <- data %>% pull(country) %>% setdiff('Cruise Ship')
data
data.world <- data %>% group_by(date) %>%
summarise(country='World',
confirmed = sum(confirmed, na.rm=T),
deaths = sum(deaths, na.rm=T),
recovered = sum(recovered, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
data %<>% rbind(data.world)
data
data %<>% mutate(current.confirmed = confirmed - deaths - recovered)
data
NA
#rate
data %<>% arrange(country, date)
n <- nrow(data)
day1 <- min(data$date)
data %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)),
new.recovered = ifelse(date == day1, NA, recovered - lag(recovered, n=1)))
data %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1))
## lower bound: death rate based on total confirmed cases
data %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1))
## death rate based on the number of death/recovered on every single day
data %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1))
View(data)
## convert from wide to long format
data.long <- data %>%
select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
current.confirmed='Current Confirmed',
recovered='Recovered',
deaths='Deaths'))
#View(data.long)
##Number of case World
world <- filter(data.long,country == 'World')
plot1 <- world %>% filter(type != 'Total Confirmed') %>%
ggplot(aes(x=date, y=count)) +
geom_area(aes(fill=type), alpha=0.5) +
labs(title=paste0('Numbers of Cases Worldwide - ', max.date.txt)) +
scale_fill_manual(values=c('red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=7),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=6),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1))
plot2 <- world %>%
ggplot(aes(x=date, y=count)) +
geom_line(aes(color=type)) +
labs(title=paste0('Numbers of Cases Worldwide (log scale) - ', max.date.txt)) +
scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=14),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=14),
axis.text=element_text(size=14),
axis.text.x=element_text(angle=45, hjust=1)) +
scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

plot2

## Current Confirmed Cases
data.world <- data %>% filter(country=='World')
n <- nrow(data.world)
plot1 <- ggplot(data.world, aes(x=date, y=current.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=new.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

View(data.world)
## a scatter plot with a smoothed line and vertical x-axis labels
plot1 <- ggplot(data.world, aes(x=date, y=deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot3 <- ggplot(data.world, aes(x=date, y=new.deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot4 <- ggplot(data.world, aes(x=date, y=new.recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot1, plot2, plot3, plot4, nrow=2)

## convert from wide to long format, for drawing area plots
rates.long <- data %>%
select(c(country, date, rate.upper, rate.lower, rate.daily)) %>%
gather(key=type, value=count, -c(country, date))
# set factor levels to show them in a desirable order
rates.long %<>% mutate(type=recode_factor(type, rate.daily='Daily',
rate.upper='Upper bound'))
## ranking by confirmed cases
data.latest.all <- data %>% filter(date == max(date)) %>%
select(country, date,confirmed, new.confirmed, current.confirmed,
recovered, deaths, new.deaths, death.rate=rate.lower) %>%
mutate(ranking = dense_rank(desc(confirmed)))
#View(data.latest.all)
k <- 20
## top 20 countries: 21 incl. 'World'
top.countries <- data.latest.all %>% filter(ranking <= k + 1) %>%
arrange(ranking) %>% pull(country) %>% as.character()
top.countries %>% setdiff('World') %>% print()
[1] "US" "India" "Brazil" "Russia" "United Kingdom"
[6] "France" "Turkey" "Italy" "Spain" "Germany"
[11] "Colombia" "Argentina" "Mexico" "Poland" "Iran"
[16] "South Africa" "Ukraine" "Peru" "Netherlands" "Indonesia"
data.latest <- data.latest.all %>% filter(!is.na(country)) %>%
mutate(country=ifelse(ranking <= k + 1, as.character(country), 'Others')) %>%
mutate(country=country %>% factor(levels=c(top.countries, 'Others')))
data.latest %<>% group_by(country) %>%
summarise(confirmed=sum(confirmed), new.confirmed=sum(new.confirmed),
current.confirmed=sum(current.confirmed),
recovered=sum(recovered), deaths=sum(deaths), new.deaths=sum(new.deaths)) %>%
mutate(death.rate=(100 * deaths/confirmed) %>% round(1))
`summarise()` ungrouping output (override with `.groups` argument)
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths, current.confirmed,recovered)) %>%
mutate(recover.rate=(100 * recovered/confirmed) %>% round(1))
data.latest
df_pop <- tbl(my_db, sql("select * from population "))
df_pop <- as.data.frame(df_pop)
df_pop <- rename(df_pop,"country"="Country")
df_pop
data.latest <- merge(x = data.latest, y = df_pop, by = "country", all.x = TRUE)
data.latest <- rename(data.latest,"population" = "Population (2020)")
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths,
current.confirmed,recovered,recover.rate,population)) %>%
mutate(confirm.rate=(100 *confirmed/population) %>% round(1))
data.latest
NA
data.latest %>% mutate(death.rate=death.rate %>% format(nsmall=1) %>% paste0('%'))
NA
NA
## convert from wide to long format, for drawing area plots
data.latest.long <- data.latest %>% filter(country!='World') %>%
gather(key=type, value=count, -country)
## set factor levels to show them with proper text and in a desirable order
data.latest.long %<>% mutate(type=recode_factor(type,
confirmed='Total Confirmed',
deaths='Total Deaths',
death.rate='Death Rate (%)',
new.confirmed='New Confirmed (compared with one day before)',
new.deaths='New Deaths (compared with one day before)',
current.confirmed='Current Confirmed',
recover.rate = 'Recover Rate(%)',
confirm.rate = 'Confirmed Rate(%)'))
#View(data.latest.long)
data.one.dem <- filter(data.latest.long,type=='Total Confirmed'
| type=='Total Deaths'
| type=='Current Confirmed')
data.two.dem <- filter(data.latest.long,type=='Death Rate (%)'
| type=='New Confirmed (compared with one day before)'
| type=='New Deaths (compared with one day before)'
| type=='Recover Rate(%)'
| type=='Confirmed Rate(%)')
data.two.dem
## bar chart
data.one.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=2, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=11),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~type, ncol=1, scales='free_y')

data.two.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=2, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=11),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~type, ncol=1, scales='free_y')

##GDP
df_gdp <- tbl(my_db, sql("select * from gdp"))
df_gdp <- as.data.frame(df_gdp)
df_gdp <- rename(df_gdp,"country"="Real GDP growth (Annual percent change)")
df_gdp <- select(df_gdp,c("country","2012","2013","2014","2015","2016","2017","2018","2019","2020","2021"))
df_gdp
df_gdp2019 <- tbl(my_db, sql("select * from gdp19"))
df_gdp2019 <- as.data.frame(df_gdp2019)
df_gdp2019
NA
#healthranking
df_healt <- tbl(my_db, sql("select * from healthranking"))
df_healt <- as.data.frame(df_healt)
df_healt <- select(df_healt,c("country","healthCareIndex"))
df_healt
#Top20Pornhub
df_pornhub <- tbl(my_db, sql("select * from Pornhub"))
df_pornhub <- as.data.frame(df_pornhub)
df_pornhub
NA
#temp
df_temp <- tbl(my_db, sql("select * from Avg_World_Temp_2020"))
df_temp <- as.data.frame(df_temp)
df_city <- select(df_temp,c("Country","City")) %>%
rename(country=Country) %>%
rename(city=City)
numofcity <- aggregate(city ~ country, data = df_city, length)
df_temp <- select(df_temp,c("Country","Apr","May","Jun","Jul","Aug")) %>%
rename(country=Country)
df_temp <- data.frame(country=df_temp[,1],avg=rowMeans(df_temp[,-1]))
df_temp <- df_temp %<>% group_by(country) %>% summarise(avg_temp = mean(avg,na.rm = TRUE))
`summarise()` ungrouping output (override with `.groups` argument)
df_temp <- df_temp %>% mutate(country=ifelse(country=="United States","US", country ) )
df_temp$avg_temp <- df_temp$avg_temp %>%
sprintf(df_temp$avg_temp, fmt = '%#.1f') %>%
as.numeric(df_temp$avg_temp)
df_temp
#Top 20 with gdp
data.longGDP <- df_gdp %>% gather(key=year, value=GDP, -c(country))
data.top <- data.latest %>% filter(country!='World')
data.top <- head(data.top,20)
#View(data.top)
data.gdp <- filter(data.longGDP,year=='2020')
#View(data.gdp)
#merge
mergcountry = function(data1,data2){
data <- merge(x = data1, y = data2, by = "country", all.x = TRUE)
return(data)
}
data.top.world <- merge(x = data.top, y = df_gdp2019, by = "country", all.x = TRUE) %>%
select(-c(code,rank,new.confirmed,new.deaths,current.confirmed,population)) %>%
rename(GDP="GDP (millions of US dollars)")
data.top.world <- merge(x = data.top.world, y = df_healt, by = "country", all.x = TRUE) %>%
rename(healthcare="healthCareIndex")
#data.top.world <- mergcountry(data.top.world, df_temp)
data.top.world <- merge(x = data.top.world, y = df_pornhub, by = "country", all.x = TRUE) %>%
rename(Pornhub = "PornhubIndex(%)")
data.top.world <- mergcountry(data.top.world, df_temp)
index <- is.na(data.top.world)
data.top.world[index] <- 0
data.top.world
#View(data.top.world)
normalize = function(data){
#return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
z <- scale(data);
tanh(z/2)
}
norm_data = as.data.frame(apply(data.top.world[,2:12],2,normalize))
corr_data <- norm_data
norm_data$country <- c("Argentina","Bangladesh","Brazil","Chile","Colombia","France","Germany","India","Iran","Italy","Mexico","Pakistan","Peru","Russia","saudi Arabia","South Africa","Spain","Turkey","United Kingdom","US")
#View(norm_data)
norm_data_plot <- select(norm_data,"country","confirm.rate","death.rate","recover.rate","healthcare","Pornhub","GDP","avg_temp")
norm_data_plot %<>% gather(key=type, value=count, -c(country))
level_order <- factor(norm_data_plot$type,
level = c("GDP","avg_temp","healthcare","recover.rate","death.rate","confirm.rate","Pornhub"))
ggplot(data = norm_data_plot, aes(x=country, y=level_order, fill=count)) +
geom_tile() +
scale_fill_gradient(low = "pink", high = "blue") +
xlab("") +
ylab("") +
theme_bw() +
theme(axis.text.x = element_text(angle = 90,vjust = 1))+
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)

NA
NA
#rank GDP
data.top.hight <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(desc(GDP)))
data.top.hight
k <- 15
top.gdp <- data.top.hight %>%
#filter(ranking <= k + 1) %>%
arrange(ranking)
top.gdp <- head(top.gdp,21)
data.top.low <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(GDP))
low.gdp.long <- data.top.low %>%
#filter(ranking <= k + 1) %>%
arrange(ranking)
View(low.gdp.long)
low.gdp <- head(low.gdp.long,23)
low.gdp
#correlation
corr_data %<>% select(c(GDP,confirm.rate,death.rate,recover.rate,healthcare,avg_temp,Pornhub))
head(corr_data)
cor(corr_data)
GDP confirm.rate death.rate recover.rate healthcare avg_temp
GDP 1.0000000 0.4350394 -0.23717777 -0.4763494 0.25183938 0.15986445
confirm.rate 0.4350394 1.0000000 -0.34002060 -0.7254358 0.46302123 -0.38053574
death.rate -0.2371778 -0.3400206 1.00000000 0.1832321 -0.16021245 0.31063014
recover.rate -0.4763494 -0.7254358 0.18323215 1.0000000 -0.73011486 0.12398936
healthcare 0.2518394 0.4630212 -0.16021245 -0.7301149 1.00000000 -0.05087304
avg_temp 0.1598645 -0.3805357 0.31063014 0.1239894 -0.05087304 1.00000000
Pornhub 0.6997223 0.4557707 -0.07822766 -0.4581646 0.35166155 0.10674647
Pornhub
GDP 0.69972226
confirm.rate 0.45577066
death.rate -0.07822766
recover.rate -0.45816457
healthcare 0.35166155
avg_temp 0.10674647
Pornhub 1.00000000
ggcorrplot(cor(corr_data),hc.order = TRUE,
outline.color = "white",
colors = c("#6D9EC1","white","#E46726"),
lab = TRUE)

df <- data.long %>% filter(country %in% top.countries) %<>%
mutate(country=country %>% factor(levels=c(top.countries)))
df %>% filter(country != 'World' & type != 'Total Confirmed') %>%
ggplot(aes(x=date, y=count, fill=type)) +
geom_area(alpha=0.5) +
# xlab('') + ylab('') +
labs(title=paste0('Numbers of COVID-19 Cases in Top 20 Countries - ',
max.date.txt)) +
scale_fill_manual(values=c('red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=12),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.4, 'cm'),
legend.text=element_text(size=12),
strip.text.x=element_text(size=12),
axis.text=element_text(size=12),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~country, ncol=4, scales='free_y')

p <- df %>% filter(country != 'World') %>%
ggplot(aes(x=date, y=count, color=type)) +
geom_line() +
labs(title=paste0('Numbers of COVID-19 Cases in Top 20 Countries (log scale) - ',
max.date.txt)) +
scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=10),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.4, 'cm'),
legend.text=element_text(size=10),
strip.text.x=element_text(size=10),
axis.text=element_text(size=10),
axis.text.x=element_text(angle=45, hjust=1)) +
scale_y_continuous(trans='log10')
p + facet_wrap(~country, ncol=4, scales='free_y')

data.world %<>% arrange(desc(date)) %>%
select(c(date, confirmed, deaths, recovered, current.confirmed,new.confirmed, new.deaths, new.recovered, rate.lower, rate.upper, rate.daily))
data.world %>%
mutate(rate.upper = rate.upper %>% format(nsmall=1) %>% paste0('\\%'),
rate.lower = rate.lower %>% format(nsmall=1) %>% paste0('\\%'),
rate.daily = rate.daily %>% format(nsmall=1) %>% paste0('\\%'))
#sars_2003
df_sars <- tbl(my_db, sql("select * from sars_2003_update"))
df_sars <- as.data.frame(df_sars)
df_sars
#datesSar <- as.Date(df_sars$Date,format = "%m/%d/%y")
#datesSar
df_sars %<>% mutate(Date = as.Date(df_sars$Date,format = "%m/%d/%y"))
df_sars
## convert from wide to long format
dataSar.long <- df_sars %>%
select(c(Date, country, Cumulative_number , Number_deaths, Number_recovered)) %>%
gather(key=type, value=count, -c(country, Date))
## set factor levels to show them in a desirable order
dataSar.long %<>% mutate(type=recode_factor(type, Cumulative_number ='Cumulative Number',
Number_deaths ='Number of deaths',
Number_recovered ='Number of recovered'))
View(dataSar.long)
g <-
ggplot(dataSar.long,aes(Date,count,color = type)) +
geom_line()+
geom_point()+
xlab("")+
ylab("")
g

NA
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdyaWQiKQ0KaW5zdGFsbC5wYWNrYWdlcygiTUxSTVBBIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJkcHJlcCIpDQojaW5zdGFsbC5wYWNrYWdlcygibm9ybWFsciIpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2djb3JycGxvdCIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkobm9ybWFscikNCmxpYnJhcnkoZ2djb3JycGxvdCkNCiNsaWJyYXJ5KE1MUk1QQSkNCiM/P3NyY19teXNxbA0KbXlfZGIgPC0gc3JjX215c3FsKA0KICBkYm5hbWUgPSAiY29yb25hdmlydXMiLA0KICBob3N0ID0gImxvY2FsaG9zdCIsDQogIHVzZXIgPSAicm9vdCIsDQogIHBhc3N3b3JkID0gIjEyMzQiDQopDQpteV9kYg0KDQojI2ltcG9ydCBkYXRhDQpkZl9jb25mIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGNvdmlkMTlfY29uZmlybWVkICIpKQ0KZGZfY29uZiA8LSBhcy5kYXRhLmZyYW1lKGRmX2NvbmYpDQpkZl9jb25mDQpkZl9kZWF0aHMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gY292aWQxOV9kZWF0aHMgIikpDQpkZl9kZWF0aHMgPC0gYXMuZGF0YS5mcmFtZShkZl9kZWF0aHMpDQpkZl9kZWF0aHMNCmRmX3JlY292ZXIgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gY292aWQxOV9yZWNvdmVyZWQgIikpDQpkZl9yZWNvdmVyIDwtIGFzLmRhdGEuZnJhbWUoZGZfcmVjb3ZlcikNCmRmX3JlY292ZXINCmBgYA0KYGBge3J9DQojI2NoZWNrIHRoZSB0aW1lIGZyYW1lIG9mIHRoZSBkYXRhDQpuLmNvbCA8LSBuY29sKGRmX2NvbmYpDQpkYXRlcyA8LSBuYW1lcyhkZl9jb25mKVs1Om4uY29sXSU+JSBtZHkoKQ0KcmFuZ2UoZGF0ZXMpDQptaW4uZGF0ZSA8LSBtaW4oZGF0ZXMpDQptYXguZGF0ZSA8LSBtYXgoZGF0ZXMpDQptaW4uZGF0ZS50eHQgPC0gbWluLmRhdGUgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KbWF4LmRhdGUudHh0IDwtIG1heC5kYXRlICU+JSBmb3JtYXQoJyVkICViICVZJykNCmBgYA0KYGBge3J9DQojY2xlYW4gZGF0YQ0KY2xlYW5EYXRhIDwtIGZ1bmN0aW9uKGRhdGEpIHsNCiAgIyMgcmVtb3ZlIHNvbWUgY29sdW1ucw0KICBkYXRhICU8PiUgc2VsZWN0KC1jKFByb3ZpbmNlLlN0YXRlLCBMYXQsIExvbmcpKSAlPiUgcmVuYW1lKGNvdW50cnk9Q291bnRyeS5SZWdpb24pDQogICMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0DQogIGRhdGEgJTw+JSBnYXRoZXIoa2V5PWRhdGUsIHZhbHVlPWNvdW50LCAtY291bnRyeSkNCiAgIyMgY29udmVydCBmcm9tIGNoYXJhY3RlciB0byBkYXRlDQogIGRhdGEgJTw+JSBtdXRhdGUoZGF0ZSA9IGRhdGUgJT4lIG1keSgpKQ0KICAjIyBhZ2dyZWdhdGUgYnkgY291bnRyeQ0KICBkYXRhICU8PiUgZ3JvdXBfYnkoY291bnRyeSwgZGF0ZSkgJT4lIHN1bW1hcmlzZShjb3VudD1zdW0oY291bnQsIG5hLnJtPVQpKSAlPiUgYXMuZGF0YS5mcmFtZSgpDQogIHJldHVybihkYXRhKQ0KfQ0KIyMgY2xlYW4gdGhlIHRocmVlIGRhdGEgc2V0cw0KZGF0YS5jb25maXJtZWQgPC0gZGZfY29uZiAlPiUgY2xlYW5EYXRhKCkgJT4lIHJlbmFtZShjb25maXJtZWQ9Y291bnQpDQpkYXRhLmRlYXRocyA8LSBkZl9kZWF0aHMgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUoZGVhdGhzPWNvdW50KQ0KZGF0YS5yZWNvdmVyZWQgPC0gZGZfcmVjb3ZlciAlPiUgY2xlYW5EYXRhKCkgJT4lIHJlbmFtZShyZWNvdmVyZWQ9Y291bnQpDQpkYXRhIDwtIGRhdGEuY29uZmlybWVkICU+JSBtZXJnZShkYXRhLmRlYXRocywgYWxsPVQpICU+JSBtZXJnZShkYXRhLnJlY292ZXJlZCwgYWxsPVQpDQpkYXRhDQojIyBjb3VudHJpZXMvcmVnaW9ucyB3aXRoIGNvbmZpcm1lZCBjYXNlcywgZXhjbC4gY3J1aXNlIHNoaXBzDQpjb3VudHJpZXMgPC0gZGF0YSAlPiUgcHVsbChjb3VudHJ5KSAlPiUgc2V0ZGlmZignQ3J1aXNlIFNoaXAnKQ0KZGF0YSANCmBgYA0KDQoNCmBgYHtyfQ0KZGF0YS53b3JsZCA8LSBkYXRhICU+JSBncm91cF9ieShkYXRlKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50cnk9J1dvcmxkJywNCiAgICAgICAgICAgIGNvbmZpcm1lZCA9IHN1bShjb25maXJtZWQsIG5hLnJtPVQpLA0KICAgICAgICAgICAgZGVhdGhzID0gc3VtKGRlYXRocywgbmEucm09VCksDQogICAgICAgICAgICByZWNvdmVyZWQgPSBzdW0ocmVjb3ZlcmVkLCBuYS5ybT1UKSkNCmRhdGEgJTw+JSByYmluZChkYXRhLndvcmxkKQ0KZGF0YQ0KZGF0YSAlPD4lIG11dGF0ZShjdXJyZW50LmNvbmZpcm1lZCA9IGNvbmZpcm1lZCAtIGRlYXRocyAtIHJlY292ZXJlZCkNCmRhdGENCg0KYGBgDQpgYGB7cn0NCiNyYXRlDQpkYXRhICU8PiUgYXJyYW5nZShjb3VudHJ5LCBkYXRlKQ0KbiA8LSBucm93KGRhdGEpDQpkYXkxIDwtIG1pbihkYXRhJGRhdGUpDQpkYXRhICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgY29uZmlybWVkIC0gbGFnKGNvbmZpcm1lZCwgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgZGVhdGhzIC0gbGFnKGRlYXRocywgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5yZWNvdmVyZWQgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgcmVjb3ZlcmVkIC0gbGFnKHJlY292ZXJlZCwgbj0xKSkpDQpkYXRhICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UobmV3LmNvbmZpcm1lZCA8IDAsIDAsIG5ldy5jb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKG5ldy5kZWF0aHMgPCAwLCAwLCBuZXcuZGVhdGhzKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShuZXcucmVjb3ZlcmVkIDwgMCwgMCwgbmV3LnJlY292ZXJlZCkpDQojIyBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGRlYXRocyBhbmQgcmVjb3ZlcmVkIGNhc2VzDQpkYXRhICU8PiUgbXV0YXRlKHJhdGUudXBwZXIgPSAoMTAwICogZGVhdGhzIC8gKGRlYXRocyArIHJlY292ZXJlZCkpICU+JSByb3VuZCgxKSkNCiMjIGxvd2VyIGJvdW5kOiBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGNvbmZpcm1lZCBjYXNlcw0KZGF0YSAlPD4lIG11dGF0ZShyYXRlLmxvd2VyID0gKDEwMCAqIGRlYXRocyAvIGNvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKQ0KIyMgZGVhdGggcmF0ZSBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIGRlYXRoL3JlY292ZXJlZCBvbiBldmVyeSBzaW5nbGUgZGF5DQpkYXRhICU8PiUgbXV0YXRlKHJhdGUuZGFpbHkgPSAoMTAwICogbmV3LmRlYXRocyAvIChuZXcuZGVhdGhzICsgbmV3LnJlY292ZXJlZCkpICU+JSByb3VuZCgxKSkNClZpZXcoZGF0YSkNCmBgYA0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdA0KZGF0YS5sb25nIDwtIGRhdGEgJT4lDQogIHNlbGVjdChjKGNvdW50cnksIGRhdGUsIGNvbmZpcm1lZCwgY3VycmVudC5jb25maXJtZWQsIHJlY292ZXJlZCwgZGVhdGhzKSkgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnksIGRhdGUpKQ0KIyMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpkYXRhLmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsIGNvbmZpcm1lZD0nVG90YWwgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jb25maXJtZWQ9J0N1cnJlbnQgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3ZlcmVkPSdSZWNvdmVyZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aHM9J0RlYXRocycpKQ0KI1ZpZXcoZGF0YS5sb25nKQ0KYGBgDQpgYGB7cn0NCiMjTnVtYmVyIG9mIGNhc2UgV29ybGQNCndvcmxkIDwtIGZpbHRlcihkYXRhLmxvbmcsY291bnRyeSA9PSAnV29ybGQnKQ0KcGxvdDEgPC0gd29ybGQgJT4lIGZpbHRlcih0eXBlICE9ICdUb3RhbCBDb25maXJtZWQnKSAlPiUNCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9Y291bnQpKSArDQogIGdlb21fYXJlYShhZXMoZmlsbD10eXBlKSwgYWxwaGE9MC41KSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdOdW1iZXJzIG9mIENhc2VzIFdvcmxkd2lkZSAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoJ3JlZCcsICdncmVlbicsICdibGFjaycpKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTcpLA0KICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQua2V5LnNpemU9dW5pdCgwLjIsICdjbScpLA0KICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT02KSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDIgPC0gd29ybGQgJT4lDQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWNvdW50KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yPXR5cGUpKSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdOdW1iZXJzIG9mIENhc2VzIFdvcmxkd2lkZSAobG9nIHNjYWxlKSAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdwdXJwbGUnLCAncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQua2V5LnNpemU9dW5pdCgwLjIsICdjbScpLA0KICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpDQojIyBzaG93IHR3byBwbG90cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MikNCmBgYA0KDQoNCmBgYHtyfQ0KcGxvdDINCmBgYA0KYGBge3J9DQojIyBDdXJyZW50IENvbmZpcm1lZCBDYXNlcw0KZGF0YS53b3JsZCA8LSBkYXRhICU+JSBmaWx0ZXIoY291bnRyeT09J1dvcmxkJykNCm4gPC0gbnJvdyhkYXRhLndvcmxkKQ0KcGxvdDEgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9Y3VycmVudC5jb25maXJtZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdDdXJyZW50IENvbmZpcm1lZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QyIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PW5ldy5jb25maXJtZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdEYWlseSBOZXcgQ29uZmlybWVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KIyMgc2hvdyB0d28gcGxvdHMgc2lkZSBieSBzaWRlDQpncmlkLmFycmFuZ2UocGxvdDEsIHBsb3QyLCBuY29sPTIpDQpWaWV3KGRhdGEud29ybGQpDQpgYGANCmBgYHtyfQ0KIyMgYSBzY2F0dGVyIHBsb3Qgd2l0aCBhIHNtb290aGVkIGxpbmUgYW5kIHZlcnRpY2FsIHgtYXhpcyBsYWJlbHMNCnBsb3QxIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PWRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0FjY3VtdWxhdGl2ZSBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1yZWNvdmVyZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdBY2N1bXVsYXRpdmUgUmVjb3ZlcmVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDMgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LmRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J05ldyBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90NCA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcucmVjb3ZlcmVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nTmV3IFJlY292ZXJlZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgZm91ciBwbG90cyB0b2dldGhlciwgd2l0aCAyIHBsb3RzIGluIGVhY2ggcm93DQpncmlkLmFycmFuZ2UocGxvdDEsIHBsb3QyLCBwbG90MywgcGxvdDQsIG5yb3c9MikNCmBgYA0KDQoNCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgZm9yIGRyYXdpbmcgYXJlYSBwbG90cw0KcmF0ZXMubG9uZyA8LSBkYXRhICU+JQ0KICBzZWxlY3QoYyhjb3VudHJ5LCBkYXRlLCByYXRlLnVwcGVyLCByYXRlLmxvd2VyLCByYXRlLmRhaWx5KSkgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnksIGRhdGUpKQ0KIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gaW4gYSBkZXNpcmFibGUgb3JkZXINCnJhdGVzLmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsIHJhdGUuZGFpbHk9J0RhaWx5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmF0ZS51cHBlcj0nVXBwZXIgYm91bmQnKSkNCmBgYA0KYGBge3J9DQojIyByYW5raW5nIGJ5IGNvbmZpcm1lZCBjYXNlcw0KZGF0YS5sYXRlc3QuYWxsIDwtIGRhdGEgJT4lIGZpbHRlcihkYXRlID09IG1heChkYXRlKSkgJT4lDQogIHNlbGVjdChjb3VudHJ5LCBkYXRlLGNvbmZpcm1lZCwgbmV3LmNvbmZpcm1lZCwgY3VycmVudC5jb25maXJtZWQsDQogICAgICAgICByZWNvdmVyZWQsIGRlYXRocywgbmV3LmRlYXRocywgZGVhdGgucmF0ZT1yYXRlLmxvd2VyKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSBkZW5zZV9yYW5rKGRlc2MoY29uZmlybWVkKSkpDQojVmlldyhkYXRhLmxhdGVzdC5hbGwpDQprIDwtIDIwDQojIyB0b3AgMjAgY291bnRyaWVzOiAyMSBpbmNsLiAnV29ybGQnDQp0b3AuY291bnRyaWVzIDwtIGRhdGEubGF0ZXN0LmFsbCAlPiUgZmlsdGVyKHJhbmtpbmcgPD0gayArIDEpICU+JQ0KICBhcnJhbmdlKHJhbmtpbmcpICU+JSBwdWxsKGNvdW50cnkpICU+JSBhcy5jaGFyYWN0ZXIoKQ0KdG9wLmNvdW50cmllcyAlPiUgc2V0ZGlmZignV29ybGQnKSAlPiUgcHJpbnQoKQ0KDQpgYGANCg0KYGBge3J9DQpkYXRhLmxhdGVzdCA8LSBkYXRhLmxhdGVzdC5hbGwgJT4lIGZpbHRlcighaXMubmEoY291bnRyeSkpICU+JQ0KICBtdXRhdGUoY291bnRyeT1pZmVsc2UocmFua2luZyA8PSBrICsgMSwgYXMuY2hhcmFjdGVyKGNvdW50cnkpLCAnT3RoZXJzJykpICU+JQ0KICBtdXRhdGUoY291bnRyeT1jb3VudHJ5ICU+JSBmYWN0b3IobGV2ZWxzPWModG9wLmNvdW50cmllcywgJ090aGVycycpKSkNCmRhdGEubGF0ZXN0ICU8PiUgZ3JvdXBfYnkoY291bnRyeSkgJT4lDQogIHN1bW1hcmlzZShjb25maXJtZWQ9c3VtKGNvbmZpcm1lZCksIG5ldy5jb25maXJtZWQ9c3VtKG5ldy5jb25maXJtZWQpLA0KICAgICAgICAgICAgY3VycmVudC5jb25maXJtZWQ9c3VtKGN1cnJlbnQuY29uZmlybWVkKSwNCiAgICAgICAgICAgIHJlY292ZXJlZD1zdW0ocmVjb3ZlcmVkKSwgZGVhdGhzPXN1bShkZWF0aHMpLCBuZXcuZGVhdGhzPXN1bShuZXcuZGVhdGhzKSkgJT4lDQogIG11dGF0ZShkZWF0aC5yYXRlPSgxMDAgKiBkZWF0aHMvY29uZmlybWVkKSAlPiUgcm91bmQoMSkpIA0KZGF0YS5sYXRlc3QNCmRhdGEubGF0ZXN0ICU8PiUgc2VsZWN0KGMoY291bnRyeSwgY29uZmlybWVkLCBkZWF0aHMsIGRlYXRoLnJhdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5jb25maXJtZWQsIG5ldy5kZWF0aHMsIGN1cnJlbnQuY29uZmlybWVkLHJlY292ZXJlZCkpICU+JQ0KICBtdXRhdGUocmVjb3Zlci5yYXRlPSgxMDAgKiByZWNvdmVyZWQvY29uZmlybWVkKSAlPiUgcm91bmQoMSkpDQpkYXRhLmxhdGVzdA0KZGZfcG9wIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHBvcHVsYXRpb24gIikpDQpkZl9wb3AgPC0gYXMuZGF0YS5mcmFtZShkZl9wb3ApDQpkZl9wb3AgPC0gcmVuYW1lKGRmX3BvcCwiY291bnRyeSI9IkNvdW50cnkiKQ0KZGZfcG9wDQpkYXRhLmxhdGVzdCA8LSBtZXJnZSh4ID0gZGF0YS5sYXRlc3QsIHkgPSBkZl9wb3AsIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpIA0KZGF0YS5sYXRlc3QgPC0gcmVuYW1lKGRhdGEubGF0ZXN0LCJwb3B1bGF0aW9uIiA9ICJQb3B1bGF0aW9uICgyMDIwKSIpDQpkYXRhLmxhdGVzdA0KZGF0YS5sYXRlc3QgICU8PiUgc2VsZWN0KGMoY291bnRyeSwgY29uZmlybWVkLCBkZWF0aHMsIGRlYXRoLnJhdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5jb25maXJtZWQsIG5ldy5kZWF0aHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkLHJlY292ZXJlZCxyZWNvdmVyLnJhdGUscG9wdWxhdGlvbikpICU+JQ0KICBtdXRhdGUoY29uZmlybS5yYXRlPSgxMDAgKmNvbmZpcm1lZC9wb3B1bGF0aW9uKSAlPiUgcm91bmQoMSkpDQpkYXRhLmxhdGVzdA0KDQpgYGANCmBgYHtyfQ0KZGF0YS5sYXRlc3QgJT4lIG11dGF0ZShkZWF0aC5yYXRlPWRlYXRoLnJhdGUgJT4lIGZvcm1hdChuc21hbGw9MSkgJT4lIHBhc3RlMCgnJScpKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCiMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0LCBmb3IgZHJhd2luZyBhcmVhIHBsb3RzDQpkYXRhLmxhdGVzdC5sb25nIDwtIGRhdGEubGF0ZXN0ICU+JSBmaWx0ZXIoY291bnRyeSE9J1dvcmxkJykgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jb3VudHJ5KQ0KIyMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIHdpdGggcHJvcGVyIHRleHQgYW5kIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpkYXRhLmxhdGVzdC5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlybWVkPSdUb3RhbCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGhzPSdUb3RhbCBEZWF0aHMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGgucmF0ZT0nRGVhdGggUmF0ZSAoJSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmNvbmZpcm1lZD0nTmV3IENvbmZpcm1lZCAoY29tcGFyZWQgd2l0aCBvbmUgZGF5IGJlZm9yZSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmRlYXRocz0nTmV3IERlYXRocyAoY29tcGFyZWQgd2l0aCBvbmUgZGF5IGJlZm9yZSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jb25maXJtZWQ9J0N1cnJlbnQgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlY292ZXIucmF0ZSA9ICdSZWNvdmVyIFJhdGUoJSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlybS5yYXRlID0gJ0NvbmZpcm1lZCBSYXRlKCUpJykpDQojVmlldyhkYXRhLmxhdGVzdC5sb25nKQ0KZGF0YS5vbmUuZGVtIDwtIGZpbHRlcihkYXRhLmxhdGVzdC5sb25nLHR5cGU9PSdUb3RhbCBDb25maXJtZWQnDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J1RvdGFsIERlYXRocycNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nQ3VycmVudCBDb25maXJtZWQnKQ0KZGF0YS50d28uZGVtIDwtIGZpbHRlcihkYXRhLmxhdGVzdC5sb25nLHR5cGU9PSdEZWF0aCBSYXRlICglKScNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nTmV3IENvbmZpcm1lZCAoY29tcGFyZWQgd2l0aCBvbmUgZGF5IGJlZm9yZSknDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J05ldyBEZWF0aHMgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdSZWNvdmVyIFJhdGUoJSknDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J0NvbmZpcm1lZCBSYXRlKCUpJykNCmRhdGEudHdvLmRlbQ0KYGBgDQoNCmBgYHtyfQ0KIyMgYmFyIGNoYXJ0DQpkYXRhLm9uZS5kZW0gJT4lIGdncGxvdChhZXMoeD1jb3VudHJ5LCB5PWNvdW50LCBmaWxsPWNvdW50cnksIGdyb3VwPWNvdW50cnkpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50LCB5PWNvdW50KSwgc2l6ZT0yLCB2anVzdD0wKSArDQogIHhsYWIoJycpICsgeWxhYignJykgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnVG9wIDIwIENvdW50cmllcyB3aXRoIE1vc3QgQ29uZmlybWVkIENhc2VzIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9J0NvdW50cnknLCBsYWJlbHM9YWVzKGNvdW50KSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTExKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2w9MSwgc2NhbGVzPSdmcmVlX3knKQ0KDQpgYGANCmBgYHtyfQ0KDQpkYXRhLnR3by5kZW0gJT4lIGdncGxvdChhZXMoeD1jb3VudHJ5LCB5PWNvdW50LCBmaWxsPWNvdW50cnksIGdyb3VwPWNvdW50cnkpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50LCB5PWNvdW50KSwgc2l6ZT0yLCB2anVzdD0wKSArDQogIHhsYWIoJycpICsgeWxhYignJykgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnVG9wIDIwIENvdW50cmllcyB3aXRoIE1vc3QgQ29uZmlybWVkIENhc2VzIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9J0NvdW50cnknLCBsYWJlbHM9YWVzKGNvdW50KSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTExKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2w9MSwgc2NhbGVzPSdmcmVlX3knKQ0KYGBgDQoNCmBgYHtyfQ0KIyNHRFANCmRmX2dkcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBnZHAiKSkNCmRmX2dkcCA8LSBhcy5kYXRhLmZyYW1lKGRmX2dkcCkNCmRmX2dkcCA8LSByZW5hbWUoZGZfZ2RwLCJjb3VudHJ5Ij0iUmVhbCBHRFAgZ3Jvd3RoIChBbm51YWwgcGVyY2VudCBjaGFuZ2UpIikNCmRmX2dkcCA8LSBzZWxlY3QoZGZfZ2RwLGMoImNvdW50cnkiLCIyMDEyIiwiMjAxMyIsIjIwMTQiLCIyMDE1IiwiMjAxNiIsIjIwMTciLCIyMDE4IiwiMjAxOSIsIjIwMjAiLCIyMDIxIikpDQpkZl9nZHANCmRmX2dkcDIwMTkgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZ2RwMTkiKSkNCmRmX2dkcDIwMTkgPC0gYXMuZGF0YS5mcmFtZShkZl9nZHAyMDE5KQ0KZGZfZ2RwMjAxOQ0KDQpgYGANCmBgYHtyfQ0KI2hlYWx0aHJhbmtpbmcNCmRmX2hlYWx0IDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGhlYWx0aHJhbmtpbmciKSkNCmRmX2hlYWx0IDwtIGFzLmRhdGEuZnJhbWUoZGZfaGVhbHQpDQpkZl9oZWFsdCA8LSBzZWxlY3QoZGZfaGVhbHQsYygiY291bnRyeSIsImhlYWx0aENhcmVJbmRleCIpKQ0KZGZfaGVhbHQNCmBgYA0KYGBge3J9DQojVG9wMjBQb3JuaHViDQpkZl9wb3JuaHViIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIFBvcm5odWIiKSkNCmRmX3Bvcm5odWIgPC0gYXMuZGF0YS5mcmFtZShkZl9wb3JuaHViKQ0KZGZfcG9ybmh1Yg0KDQpgYGANCg0KDQpgYGB7cn0NCiN0ZW1wDQpkZl90ZW1wIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIEF2Z19Xb3JsZF9UZW1wXzIwMjAiKSkNCmRmX3RlbXAgPC0gYXMuZGF0YS5mcmFtZShkZl90ZW1wKQ0KZGZfY2l0eSA8LSBzZWxlY3QoZGZfdGVtcCxjKCJDb3VudHJ5IiwiQ2l0eSIpKSAlPiUNCiAgcmVuYW1lKGNvdW50cnk9Q291bnRyeSkgJT4lIA0KICByZW5hbWUoY2l0eT1DaXR5KQ0KbnVtb2ZjaXR5IDwtIGFnZ3JlZ2F0ZShjaXR5IH4gY291bnRyeSwgZGF0YSA9IGRmX2NpdHksIGxlbmd0aCkNCmRmX3RlbXAgPC0gc2VsZWN0KGRmX3RlbXAsYygiQ291bnRyeSIsIkFwciIsIk1heSIsIkp1biIsIkp1bCIsIkF1ZyIpKSAlPiUNCiAgcmVuYW1lKGNvdW50cnk9Q291bnRyeSkNCmRmX3RlbXAgPC0gZGF0YS5mcmFtZShjb3VudHJ5PWRmX3RlbXBbLDFdLGF2Zz1yb3dNZWFucyhkZl90ZW1wWywtMV0pKQ0KZGZfdGVtcCA8LSBkZl90ZW1wICU8PiUgZ3JvdXBfYnkoY291bnRyeSkgJT4lIHN1bW1hcmlzZShhdmdfdGVtcCA9IG1lYW4oYXZnLG5hLnJtID0gVFJVRSkpDQpkZl90ZW1wIDwtIGRmX3RlbXAgJT4lIG11dGF0ZShjb3VudHJ5PWlmZWxzZShjb3VudHJ5PT0iVW5pdGVkIFN0YXRlcyIsIlVTIiwgY291bnRyeSApICkgDQpkZl90ZW1wJGF2Z190ZW1wIDwtIGRmX3RlbXAkYXZnX3RlbXAgJT4lIA0KICBzcHJpbnRmKGRmX3RlbXAkYXZnX3RlbXAsIGZtdCA9ICclIy4xZicpICU+JQ0KICBhcy5udW1lcmljKGRmX3RlbXAkYXZnX3RlbXApDQpkZl90ZW1wDQpgYGANCg0KDQoNCmBgYHtyfQ0KI1RvcCAyMCB3aXRoIGdkcA0KZGF0YS5sb25nR0RQIDwtIGRmX2dkcCAlPiUgZ2F0aGVyKGtleT15ZWFyLCB2YWx1ZT1HRFAsIC1jKGNvdW50cnkpKQ0KZGF0YS50b3AgPC0gZGF0YS5sYXRlc3QgJT4lIGZpbHRlcihjb3VudHJ5IT0nV29ybGQnKQ0KZGF0YS50b3AgPC0gaGVhZChkYXRhLnRvcCwyMCkNCiNWaWV3KGRhdGEudG9wKQ0KZGF0YS5nZHAgPC0gZmlsdGVyKGRhdGEubG9uZ0dEUCx5ZWFyPT0nMjAyMCcpDQojVmlldyhkYXRhLmdkcCkNCiNtZXJnZQ0KbWVyZ2NvdW50cnkgPSBmdW5jdGlvbihkYXRhMSxkYXRhMil7DQogIGRhdGEgPC0gbWVyZ2UoeCA9IGRhdGExLCB5ID0gZGF0YTIsIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpIA0KICByZXR1cm4oZGF0YSkNCn0NCmRhdGEudG9wLndvcmxkIDwtIG1lcmdlKHggPSBkYXRhLnRvcCwgeSA9IGRmX2dkcDIwMTksIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpICU+JSANCiAgc2VsZWN0KC1jKGNvZGUscmFuayxuZXcuY29uZmlybWVkLG5ldy5kZWF0aHMsY3VycmVudC5jb25maXJtZWQscG9wdWxhdGlvbikpICU+JSANCiAgcmVuYW1lKEdEUD0iR0RQIChtaWxsaW9ucyBvZiBVUyBkb2xsYXJzKSIpDQoNCmRhdGEudG9wLndvcmxkIDwtIG1lcmdlKHggPSBkYXRhLnRvcC53b3JsZCwgeSA9IGRmX2hlYWx0LCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSAlPiUNCiAgcmVuYW1lKGhlYWx0aGNhcmU9ImhlYWx0aENhcmVJbmRleCIpDQojZGF0YS50b3Aud29ybGQgPC0gbWVyZ2NvdW50cnkoZGF0YS50b3Aud29ybGQsIGRmX3RlbXApDQoNCmRhdGEudG9wLndvcmxkIDwtIG1lcmdlKHggPSBkYXRhLnRvcC53b3JsZCwgeSA9IGRmX3Bvcm5odWIsIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpICU+JQ0KICByZW5hbWUoUG9ybmh1YiA9ICJQb3JuaHViSW5kZXgoJSkiKQ0KDQpkYXRhLnRvcC53b3JsZCA8LSBtZXJnY291bnRyeShkYXRhLnRvcC53b3JsZCwgZGZfdGVtcCkNCmluZGV4IDwtIGlzLm5hKGRhdGEudG9wLndvcmxkKQ0KZGF0YS50b3Aud29ybGRbaW5kZXhdIDwtIDANCmRhdGEudG9wLndvcmxkDQojVmlldyhkYXRhLnRvcC53b3JsZCkNCg0Kbm9ybWFsaXplID0gZnVuY3Rpb24oZGF0YSl7DQogICNyZXR1cm4gKChkYXRhIC0gbWluKGRhdGEsbmEucm0gPSBUUlVFKSkvKG1heChkYXRhLG5hLnJtID0gVFJVRSkgLSBtaW4oZGF0YSxuYS5ybSA9IFRSVUUpKSkNCiAgeiA8LSBzY2FsZShkYXRhKTsNCiAgdGFuaCh6LzIpDQp9DQpub3JtX2RhdGEgPSBhcy5kYXRhLmZyYW1lKGFwcGx5KGRhdGEudG9wLndvcmxkWywyOjEyXSwyLG5vcm1hbGl6ZSkpDQpjb3JyX2RhdGEgPC0gbm9ybV9kYXRhDQpub3JtX2RhdGEkY291bnRyeSA8LSBjKCJBcmdlbnRpbmEiLCJCYW5nbGFkZXNoIiwiQnJhemlsIiwiQ2hpbGUiLCJDb2xvbWJpYSIsIkZyYW5jZSIsIkdlcm1hbnkiLCJJbmRpYSIsIklyYW4iLCJJdGFseSIsIk1leGljbyIsIlBha2lzdGFuIiwiUGVydSIsIlJ1c3NpYSIsInNhdWRpIEFyYWJpYSIsIlNvdXRoIEFmcmljYSIsIlNwYWluIiwiVHVya2V5IiwiVW5pdGVkIEtpbmdkb20iLCJVUyIpDQojVmlldyhub3JtX2RhdGEpDQoNCg0Kbm9ybV9kYXRhX3Bsb3QgPC0gc2VsZWN0KG5vcm1fZGF0YSwiY291bnRyeSIsImNvbmZpcm0ucmF0ZSIsImRlYXRoLnJhdGUiLCJyZWNvdmVyLnJhdGUiLCJoZWFsdGhjYXJlIiwiUG9ybmh1YiIsIkdEUCIsImF2Z190ZW1wIikNCm5vcm1fZGF0YV9wbG90ICU8PiUgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoY291bnRyeSkpDQpsZXZlbF9vcmRlciA8LSBmYWN0b3Iobm9ybV9kYXRhX3Bsb3QkdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgPSBjKCJHRFAiLCJhdmdfdGVtcCIsImhlYWx0aGNhcmUiLCJyZWNvdmVyLnJhdGUiLCJkZWF0aC5yYXRlIiwiY29uZmlybS5yYXRlIiwiUG9ybmh1YiIpKQ0KZ2dwbG90KGRhdGEgPSBub3JtX2RhdGFfcGxvdCwgYWVzKHg9Y291bnRyeSwgeT1sZXZlbF9vcmRlciwgZmlsbD1jb3VudCkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAicGluayIsIGhpZ2ggPSAiYmx1ZSIpICsNCiAgeGxhYigiIikgKw0KICB5bGFiKCIiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLHZqdXN0ID0gMSkpKw0KICB0aGVtZSgNCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgI2xlZ2VuZC5wb3NpdGlvbiA9ICJub25lIg0KICApDQoNCiAgDQpgYGANCg0KDQpgYGB7cn0NCiNyYW5rIEdEUA0KZGF0YS50b3AuaGlnaHQgPC0gZGF0YS5nZHAgJT4lIHNlbGVjdChjb3VudHJ5LCB5ZWFyLEdEUCkgJT4lDQogIG11dGF0ZShyYW5raW5nID0gZGVuc2VfcmFuayhkZXNjKEdEUCkpKQ0KZGF0YS50b3AuaGlnaHQNCmsgPC0gMTUNCnRvcC5nZHAgPC0gZGF0YS50b3AuaGlnaHQgJT4lIA0KICAjZmlsdGVyKHJhbmtpbmcgPD0gayArIDEpICU+JSANCiAgYXJyYW5nZShyYW5raW5nKQ0KdG9wLmdkcCA8LSBoZWFkKHRvcC5nZHAsMjEpDQpkYXRhLnRvcC5sb3cgPC0gZGF0YS5nZHAgJT4lIHNlbGVjdChjb3VudHJ5LCB5ZWFyLEdEUCkgJT4lDQogIG11dGF0ZShyYW5raW5nID0gZGVuc2VfcmFuayhHRFApKQ0KbG93LmdkcC5sb25nIDwtIGRhdGEudG9wLmxvdyAlPiUgDQogICNmaWx0ZXIocmFua2luZyA8PSBrICsgMSkgJT4lIA0KICBhcnJhbmdlKHJhbmtpbmcpDQpWaWV3KGxvdy5nZHAubG9uZykNCmxvdy5nZHAgPC0gaGVhZChsb3cuZ2RwLmxvbmcsMjMpDQpsb3cuZ2RwDQpgYGANCg0KDQoNCmBgYHtyfQ0KI2NvcnJlbGF0aW9uDQpjb3JyX2RhdGEgJTw+JSBzZWxlY3QoYyhHRFAsY29uZmlybS5yYXRlLGRlYXRoLnJhdGUscmVjb3Zlci5yYXRlLGhlYWx0aGNhcmUsYXZnX3RlbXAsUG9ybmh1YikpDQpoZWFkKGNvcnJfZGF0YSkNCmNvcihjb3JyX2RhdGEpDQpnZ2NvcnJwbG90KGNvcihjb3JyX2RhdGEpLGhjLm9yZGVyID0gVFJVRSwNCiAgICAgICAgICAgb3V0bGluZS5jb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgIGNvbG9ycyA9IGMoIiM2RDlFQzEiLCJ3aGl0ZSIsIiNFNDY3MjYiKSwNCiAgICAgICAgICAgbGFiID0gVFJVRSkNCmBgYA0KYGBge3J9DQpkZiA8LSBkYXRhLmxvbmcgJT4lIGZpbHRlcihjb3VudHJ5ICVpbiUgdG9wLmNvdW50cmllcykgJTw+JQ0KICBtdXRhdGUoY291bnRyeT1jb3VudHJ5ICU+JSBmYWN0b3IobGV2ZWxzPWModG9wLmNvdW50cmllcykpKQ0KZGYgJT4lIGZpbHRlcihjb3VudHJ5ICE9ICdXb3JsZCcgJiB0eXBlICE9ICdUb3RhbCBDb25maXJtZWQnKSAlPiUNCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9Y291bnQsIGZpbGw9dHlwZSkpICsNCiAgZ2VvbV9hcmVhKGFscGhhPTAuNSkgKw0KIyB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ051bWJlcnMgb2YgQ09WSUQtMTkgQ2FzZXMgaW4gVG9wIDIwIENvdW50cmllcyAtICcsDQogICAgICAgICAgICAgICAgICAgIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoJ3JlZCcsICdncmVlbicsICdibGFjaycpKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmtleS5zaXplPXVuaXQoMC40LCAnY20nKSwNCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICAgICAgICBzdHJpcC50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIGZhY2V0X3dyYXAofmNvdW50cnksIG5jb2w9NCwgc2NhbGVzPSdmcmVlX3knKQ0KDQpgYGANCmBgYHtyfQ0KcCA8LSBkZiAlPiUgZmlsdGVyKGNvdW50cnkgIT0gJ1dvcmxkJykgJT4lDQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWNvdW50LCBjb2xvcj10eXBlKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdOdW1iZXJzIG9mIENPVklELTE5IENhc2VzIGluIFRvcCAyMCBDb3VudHJpZXMgKGxvZyBzY2FsZSkgLSAnLA0KICAgICAgICAgICAgICAgICAgICBtYXguZGF0ZS50eHQpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygncHVycGxlJywgJ3JlZCcsICdncmVlbicsICdibGFjaycpKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmtleS5zaXplPXVuaXQoMC40LCAnY20nKSwNCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICBzdHJpcC50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nMTAnKQ0KcCArIGZhY2V0X3dyYXAofmNvdW50cnksIG5jb2w9NCwgc2NhbGVzPSdmcmVlX3knKQ0KYGBgDQpgYGB7cn0NCmRhdGEud29ybGQgJTw+JSBhcnJhbmdlKGRlc2MoZGF0ZSkpICU+JQ0KICBzZWxlY3QoYyhkYXRlLCBjb25maXJtZWQsIGRlYXRocywgcmVjb3ZlcmVkLCBjdXJyZW50LmNvbmZpcm1lZCxuZXcuY29uZmlybWVkLCBuZXcuZGVhdGhzLCBuZXcucmVjb3ZlcmVkLCByYXRlLmxvd2VyLCByYXRlLnVwcGVyLCByYXRlLmRhaWx5KSkNCmRhdGEud29ybGQgJT4lDQogIG11dGF0ZShyYXRlLnVwcGVyID0gcmF0ZS51cHBlciAlPiUgZm9ybWF0KG5zbWFsbD0xKSAlPiUgcGFzdGUwKCdcXCUnKSwNCiAgICAgICAgIHJhdGUubG93ZXIgPSByYXRlLmxvd2VyICU+JSBmb3JtYXQobnNtYWxsPTEpICU+JSBwYXN0ZTAoJ1xcJScpLA0KICAgICAgICAgcmF0ZS5kYWlseSA9IHJhdGUuZGFpbHkgJT4lIGZvcm1hdChuc21hbGw9MSkgJT4lIHBhc3RlMCgnXFwlJykpIA0KYGBgDQpgYGB7cn0NCiNzYXJzXzIwMDMNCmRmX3NhcnMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gc2Fyc18yMDAzX3VwZGF0ZSIpKQ0KZGZfc2FycyA8LSBhcy5kYXRhLmZyYW1lKGRmX3NhcnMpDQpkZl9zYXJzDQpgYGANCg0KDQpgYGB7cn0NCiMjIGNvbnZlcnQgZnJvbSBjaGFyYWN0ZXIgdG8gZGF0ZQ0KI2RhdGVzU2FyIDwtIGFzLkRhdGUoZGZfc2FycyREYXRlLGZvcm1hdCA9ICIlbS8lZC8leSIpDQoNCmRmX3NhcnMgJTw+JSAgbXV0YXRlKERhdGUgPSBhcy5EYXRlKGRmX3NhcnMkRGF0ZSxmb3JtYXQgPSAiJW0vJWQvJXkiKSkNCmRmX3NhcnMNCmBgYA0KDQpgYGB7cn0NCiMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0DQpkYXRhU2FyLmxvbmcgPC0gZGZfc2FycyAlPiUNCiAgc2VsZWN0KGMoRGF0ZSwgY291bnRyeSwgQ3VtdWxhdGl2ZV9udW1iZXIgLCBOdW1iZXJfZGVhdGhzLCBOdW1iZXJfcmVjb3ZlcmVkKSkgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnksIERhdGUpKQ0KIyMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpkYXRhU2FyLmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsIEN1bXVsYXRpdmVfbnVtYmVyID0nQ3VtdWxhdGl2ZSBOdW1iZXInLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOdW1iZXJfZGVhdGhzID0nTnVtYmVyIG9mIGRlYXRocycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlcl9yZWNvdmVyZWQgPSdOdW1iZXIgb2YgcmVjb3ZlcmVkJykpDQpWaWV3KGRhdGFTYXIubG9uZykNCiANCmBgYA0KDQoNCg0KDQpgYGB7cn0NCmcgPC0NCiAgZ2dwbG90KGRhdGFTYXIubG9uZyxhZXMoRGF0ZSxjb3VudCxjb2xvciA9IHR5cGUpKSArDQogIGdlb21fbGluZSgpKw0KICBnZW9tX3BvaW50KCkrDQogIHhsYWIoIiIpKw0KICB5bGFiKCIiKQ0KZw0KICANCmBgYA0KDQoNCg0K